package com.vesystem.vegua.modules.common.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.vesystem.vegua.config.GlobalConfig;
import com.vesystem.vegua.exceptionConfig.ErrorCode;
import com.vesystem.vegua.exceptionConfig.ParameterInvalid;
import com.vesystem.vegua.modules.common.dto.SysUserDto;
import com.vesystem.vegua.modules.common.dto.TunnelDto;
import com.vesystem.vegua.modules.common.dto.UserSessionDto;
import com.vesystem.vegua.modules.common.entity.SysUser;
import com.vesystem.vegua.modules.common.mapper.SysUserMapper;
import com.vesystem.vegua.modules.common.service.ISysUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vesystem.vegua.utils.JwtToken;
import com.vesystem.vegua.config.ServerListener;
import com.vesystem.vegua.websocket.WebSocketTunnel;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.GuacamoleTunnel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author hcy
 * @since 2020-12-08
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {
    @Autowired
    private SysUserMapper sysUserMapper;

    public String userLogin(String loginname,String password){
        QueryWrapper<SysUser> qw = new QueryWrapper<>();
        qw.eq("loginname",loginname);
        SysUser dbUser = sysUserMapper.selectOne(qw);
        if (!Objects.isNull( dbUser ) ){
            if ( Objects.equals( dbUser.getPassword(),password ) ){
                //账号密码正确
                String token = JwtToken.createToken(loginname,dbUser.getId(),dbUser.getRole());
                ServerListener.TOKEN_CACHE.put(loginname,token);
                return token;
            }
        }
        throw new ParameterInvalid(ErrorCode.LOGINNAME_OR_PASSWORD_ERROR);
    }

    public String refreshToken(HttpServletRequest request){
        String loginname = JwtToken.getUserLoginname(request);
        String newToken = JwtToken.refreshToken(request);
        ServerListener.TOKEN_CACHE.put( loginname, newToken);
        return newToken;
    }

    public void userLogout(HttpServletRequest request){
        String loginname = JwtToken.getUserLoginname(request);
        ServerListener.TOKEN_CACHE.remove(loginname);
    }

    public void addUser(SysUserDto user){
        QueryWrapper<SysUser> qw = new QueryWrapper<>();
        qw.eq("loginname",user.getLoginname());
        SysUser dbUser = sysUserMapper.selectOne(qw);
        if ( Objects.isNull( dbUser ) ){
            user.setCreateTime(new Date());
            if (StringUtils.isEmpty( user.getAlias() )){
                user.setAlias( user.getLoginname() );
            }
            user.setStatus(1);
            sysUserMapper.insert(user);
        }else {
            throw new ParameterInvalid(ErrorCode.LOGINNAME_ALERADY_EXIST);
        }
    }

    public void updateSelfPassword(String oldPassword,String newPassword,HttpServletRequest request){
        Integer userId = JwtToken.getUserId(request);
        SysUser dbUser = sysUserMapper.selectById(userId);
        if ( !Objects.equals(oldPassword,dbUser.getPassword()) ){
            throw new ParameterInvalid(ErrorCode.OLD_PASSWORD_ERROR);
        }
        dbUser.setPassword( newPassword );
        sysUserMapper.updateById(dbUser);
    }

    public void updateUserPasswordById(SysUserDto user){
        SysUser dbUser = sysUserMapper.selectById(user.getId());
        if ( Objects.isNull( dbUser ) ){
            throw new ParameterInvalid(ErrorCode.METHOD_ARGUMENT_NOT_VALID);
        }
        if ( StringUtils.isEmpty( user.getPassword() ) ){
            dbUser.setPassword( GlobalConfig.INIT_PWD );
            sysUserMapper.updateById(dbUser);
            ServerListener.TOKEN_CACHE.remove(dbUser.getLoginname());
            return;
        }
        if ( !Objects.equals( user.getPassword(),dbUser.getPassword() ) ){
            dbUser.setPassword( user.getPassword() );
            sysUserMapper.updateById(dbUser);
            ServerListener.TOKEN_CACHE.remove(dbUser.getLoginname());
        }
    }
    public void updateUserStatusById(SysUserDto user){
        SysUser dbUser = sysUserMapper.selectById(user.getId());
        if ( Objects.isNull( dbUser ) ){
            throw new ParameterInvalid(ErrorCode.METHOD_ARGUMENT_NOT_VALID);
        }
        if ( !Objects.equals( user.getStatus(),dbUser.getStatus() ) ){
            dbUser.setStatus( user.getStatus() );
            sysUserMapper.updateById(dbUser);
            if ( user.getStatus().intValue() == 0 ){
                ServerListener.TOKEN_CACHE.remove(dbUser.getLoginname());
            }
        }
    }
    public void updateUserAliasById(SysUserDto user){
        SysUser dbUser = sysUserMapper.selectById(user.getId());
        if ( Objects.isNull( dbUser ) ){
            throw new ParameterInvalid(ErrorCode.METHOD_ARGUMENT_NOT_VALID);
        }
        if ( !Objects.equals( user.getAlias(),dbUser.getAlias() ) ){
            dbUser.setAlias( user.getAlias() );
            sysUserMapper.updateById(dbUser);
        }
    }
    public void deleteUserById(List<Integer> userIds){
        List<SysUser> dbUsers = sysUserMapper.selectBatchIds(userIds);
        sysUserMapper.deleteBatchIds(userIds);
        for (SysUser b:dbUsers){
            ServerListener.TOKEN_CACHE.remove(b.getLoginname());
        }
    }

    public Page<SysUser> adminGetUserList(Page<SysUser> page,String alias,String loginname){
        QueryWrapper<SysUser> qw = new QueryWrapper<>();
        qw.like("alias",alias).like("loginname",loginname);
        return sysUserMapper.selectPage(page,qw);
    }

    public SysUser getSysUserDetailById(Integer userId){
        SysUser dbUser = sysUserMapper.selectById(userId);
        if ( Objects.isNull( dbUser ) ){
            throw new ParameterInvalid(ErrorCode.DATA_NOT_EXIST);
        }
        return dbUser;
    }

    public List<UserSessionDto> getSessionList(String loginName){
        Set<String> loginSet = ServerListener.TOKEN_CACHE.keySet();
        List<String> filterSet = loginSet.stream().filter( s ->s.indexOf(loginName)>-1).collect(Collectors.toList());
        if ( filterSet.size() == 0 ){
            return new ArrayList<>();
        }
        QueryWrapper<SysUser> qw = new QueryWrapper<>();
        qw.in("loginname",filterSet);
        List<SysUser> db_users = sysUserMapper.selectList(qw);
        List<UserSessionDto> list = new ArrayList<>(db_users.size());
        for (SysUser u:db_users){
            UserSessionDto sessionDto = new UserSessionDto();
            sessionDto.setUser(u);
            String token = ServerListener.TOKEN_CACHE.get(u.getLoginname());
            sessionDto.setToken( token );
            sessionDto.setExpiDate( JwtToken.getClaims(token).getExpiration().getTime()/1000L );
            sessionDto.setIssueDate( JwtToken.getClaims(token).getIssuedAt().getTime() /1000L );
            TunnelDto tunnelDto = WebSocketTunnel.guacamoleTunnelMap.get("1_" +u.getLoginname());
            if ( tunnelDto ==null ){
                sessionDto.setServerIp("--");
                sessionDto.setRemoteApp("--");
            }else {
                if ( StringUtils.isEmpty( tunnelDto.getServerIp() ) ){
                    sessionDto.setServerIp("--");
                    sessionDto.setRemoteApp("--");
                }else {
                    sessionDto.setServerIp(tunnelDto.getServerIp());
                    if ( StringUtils.isEmpty( tunnelDto.getRemoteApp() ) ){
                        sessionDto.setRemoteApp("desktop");
                    }else {
                        sessionDto.setRemoteApp(tunnelDto.getRemoteApp());
                    }
                }
            }
            list.add( sessionDto );
        }
        return list;
    }

    public void unsubscribeSession(String loginname){
        TunnelDto tunnelDto = WebSocketTunnel.guacamoleTunnelMap.get("1_"+loginname);
        if ( tunnelDto !=null ){
            GuacamoleTunnel tunnel = tunnelDto.getGuacamoleTunnel();
            if ( tunnel !=null ){
                if ( tunnel.isOpen() ){
                    try {
                        tunnel.close();
                        WebSocketTunnel.guacamoleTunnelMap.remove("1_"+loginname);
                    } catch (GuacamoleException e) {
                        e.printStackTrace();
                        log.error(e.getLocalizedMessage(),e);
                    }
                }
            }
        }
        ServerListener.TOKEN_CACHE.remove(loginname);
    }

    public void blockedAccount(String loginname){
        unsubscribeSession(loginname);
        QueryWrapper<SysUser> qw = new QueryWrapper<>();
        qw.eq("loginname",loginname);
        SysUser db_user = sysUserMapper.selectOne(qw);
        db_user.setStatus(0);
        sysUserMapper.updateById( db_user );
    }


}
